﻿using RakutenProxy.Model.NopCommerce;
using RakutenProxy.Model.Rakuten;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;

namespace RakutenProxy.DataConverter
{
    public class RakNopCategoryConverter : RakNopDataConverter<RakutenProxy.Model.Rakuten.Category, RakutenProxy.Model.NopCommerce.Category>
    {
        public RakNopCategoryConverter(DbContext sourceDbContext, DbContext targetDbContext)
            : base(sourceDbContext, targetDbContext)
        {
        }

        public override void Migrate()
        {
            var rakDbContext = (RakutenDbContext)SourceContext;
            var nopDbContext = (NopCommerceEntities)TargetContext;


            var mappedCategoryIds = rakDbContext.Rakuten_NopCommerce_Category_Maps.Select(m => m.RakutenCategoryId);

            var notMappedCategories = rakDbContext.Categories.Where(c => !mappedCategoryIds.Contains(c.CategoryId));
            var mappedCategories = rakDbContext.Categories.Except(notMappedCategories);

            // map rak category with nop category
            foreach (var rakCategory in notMappedCategories)
            {
                var nopCategory = GetObjectFromSource(rakCategory);                
                nopDbContext.Categories.Add(nopCategory);
                nopDbContext.SaveChanges();

                var categoryMap = new Rakuten_NopCommerce_Category_Map { RakutenCategoryId = rakCategory.CategoryId, NopCommerceCategoryId = nopCategory.Id };
                rakDbContext.Rakuten_NopCommerce_Category_Maps.Add(categoryMap);

                var urlRecord = AddOrUpdateUrlRecord(nopCategory);
                if (urlRecord.Id == 0)
                {
                    nopDbContext.UrlRecords.Add(urlRecord);
                }
            }

            // update exists map
            foreach (var rakCategory in mappedCategories)
            {
                var map = rakDbContext.Rakuten_NopCommerce_Category_Maps.FirstOrDefault(m => m.RakutenCategoryId == rakCategory.CategoryId);
                if (map != null)
                {
                    var nopCategory = nopDbContext.Categories.FirstOrDefault(c => c.Id == map.NopCommerceCategoryId);
                    if (nopCategory != null && !rakCategory.Equals(nopCategory))
                    {
                        UpdateObjectFromSource(nopCategory, rakCategory);
                    }
                }
            }

            rakDbContext.SaveChanges();
            nopDbContext.SaveChanges();

            MapParentRelationship();
            MapDisplayOrder();
        }

        private void MapParentRelationship()
        {
            var rakDbContext = (RakutenDbContext)SourceContext;
            var nopDbContext = (NopCommerceEntities)TargetContext;
            var childCategories = rakDbContext.Categories.Where(c => c.ParentCategoryId != null);
            foreach (var category in childCategories)
            {
                var mapParentCategory = rakDbContext.Rakuten_NopCommerce_Category_Maps.First(m => m.RakutenCategoryId == category.ParentCategoryId.Value);
                var mapCategory = rakDbContext.Rakuten_NopCommerce_Category_Maps.First(m => m.RakutenCategoryId == category.CategoryId);

                var nopCategory = nopDbContext.Categories.First(n => n.Id == mapCategory.NopCommerceCategoryId);
                nopCategory.ParentCategoryId = mapParentCategory.NopCommerceCategoryId;
            }

            nopDbContext.SaveChanges();
        }

        private void MapDisplayOrder()
        {
            var nopDbContext = (NopCommerceEntities)TargetContext;
            var parentCategories = nopDbContext.Categories.Where(n => n.ParentCategoryId == 0).ToList();
            for (int i = 0; i < parentCategories.Count; i++)
            {
                var pCategory = parentCategories[i];
                pCategory.DisplayOrder = i + 1;
                var children = nopDbContext.Categories.Where(n => n.ParentCategoryId == pCategory.Id).ToList();
                for (int j = 0; j < children.Count; j++)
                {
                    var category = children[j];
                    category.DisplayOrder = j + 1;
                }
            }

            nopDbContext.SaveChanges();
        }

        private UrlRecord AddOrUpdateUrlRecord(Model.NopCommerce.Category category)
        {
            var nopDbContext = (NopCommerceEntities)TargetContext;
            var slug =Utils.UniformUrl(category.Name);
            var urlRecord = nopDbContext.UrlRecords.FirstOrDefault(u => u.Slug == slug);
            if (urlRecord == null)
            {
                urlRecord = new UrlRecord { EntityId = category.Id, EntityName = "Category", Slug = Utils.UniformUrl(category.Name), IsActive = true };
            }
            else
            {
                urlRecord.EntityId = category.Id;
            }

            return urlRecord;
        }

        public override void MigrateOneRecord(int id)
        {
            var rakDbContext = (RakutenDbContext)SourceContext;
            var nopDbContext = (NopCommerceEntities)TargetContext;

            var map = rakDbContext.Rakuten_NopCommerce_Category_Maps.FirstOrDefault(m => m.RakutenCategoryId == id);
            if (map == null)
            {
                var rakCategory = rakDbContext.Categories.First(m => m.CategoryId == id);
                var nopCategory = GetObjectFromSource(rakCategory);
                nopDbContext.Categories.Add(nopCategory);
                nopDbContext.SaveChanges();

                map = new Rakuten_NopCommerce_Category_Map { RakutenCategoryId = id, NopCommerceCategoryId = nopCategory.Id };
                rakDbContext.Rakuten_NopCommerce_Category_Maps.Add(map);

                var urlRecord = AddOrUpdateUrlRecord(nopCategory);
                if (urlRecord.Id == 0)
                {
                    nopDbContext.UrlRecords.Add(urlRecord);
                }
            }
            else
            {
                var nopCategory = nopDbContext.Categories.First(n => n.Id == map.NopCommerceCategoryId);
                var rakCategory = rakDbContext.Categories.First(n => n.CategoryId == map.RakutenCategoryId);
                UpdateObjectFromSource(nopCategory, rakCategory);
            }

            rakDbContext.SaveChanges();
            nopDbContext.SaveChanges();
        }

        protected override Model.NopCommerce.Category GetObjectFromSource(Model.Rakuten.Category source)
        {
            var rakDbContext = (RakutenDbContext)SourceContext;
            var nopCategory = new Model.NopCommerce.Category();
            nopCategory.Name = Translate(source.CategoryName);
            nopCategory.CategoryTemplateId = 1;
            nopCategory.AllowCustomersToSelectPageSize = true;
            nopCategory.PageSize = 4;
            nopCategory.PageSizeOptions = "8, 4, 12";
            nopCategory.IncludeInTopMenu = true;
            nopCategory.Published = true;
            nopCategory.DisplayOrder = 1;
            nopCategory.CreatedOnUtc = DateTime.UtcNow;
            nopCategory.UpdatedOnUtc = DateTime.UtcNow;

            // if source category is a child category => map parent category id
            if (source.ParentCategoryId != null)
            {
                var categoryMap = rakDbContext.Rakuten_NopCommerce_Category_Maps.FirstOrDefault(m => m.RakutenCategoryId == source.ParentCategoryId.Value);
                if (categoryMap != null)
                {
                    nopCategory.ParentCategoryId = categoryMap.NopCommerceCategoryId;
                }
            }

            return nopCategory;
        }

        protected override void UpdateObjectFromSource(Model.NopCommerce.Category target, Model.Rakuten.Category source)
        {
            target.Name = Translate(source.CategoryName);
            target.ParentCategoryId = source.ParentCategoryId.HasValue ? source.ParentCategoryId.Value : 0;
        }

        public override void RemoveOneRecord(int id)
        {
            var rakDbContext = (RakutenDbContext)SourceContext;
            var nopDbContext = (NopCommerceEntities)TargetContext;

            var map = rakDbContext.Rakuten_NopCommerce_Category_Maps.FirstOrDefault(m => m.RakutenCategoryId == id);
            if (map != null)
            {
                var nopCategory = nopDbContext.Categories.FirstOrDefault(c => c.Id == map.NopCommerceCategoryId);
                if (nopCategory != null)
                {
                    nopDbContext.Categories.Remove(nopCategory);

                    // if this is a parent category
                    if (nopCategory.ParentCategoryId == null)
                    {
                        var childCategoryIds = nopDbContext.Categories.Where(c => c.ParentCategoryId == id).
                            Select(c => rakDbContext.Rakuten_NopCommerce_Category_Maps.FirstOrDefault(m => m.NopCommerceCategoryId == c.Id)).Select(c => c.RakutenCategoryId);
                        RemoveManyRecords(childCategoryIds);
                    }

                    rakDbContext.Rakuten_NopCommerce_Category_Maps.Remove(map);
                }

                rakDbContext.SaveChanges();
                nopDbContext.SaveChanges();
            }            
        }
    }
}